package com.fafian.srjm;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fafian.publish.NoListenerException;
import com.fafian.publish.PublishBus;
import com.fafian.publish.SynchronousBusException;
import com.fafian.publish.json.JSONStringBus;
import com.fafian.publish.json.JSONWorkerDescription;
import com.fafian.publish.json.JSONWorkerGenerator;
import com.fafian.publish.json.exceptions.JSONWorkerGeneratorException;

import fi.iki.elonen.NanoHTTPD;

public class SRJMCore extends NanoHTTPD {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private JSONStringBus bus;
	private boolean run;
	private final SRJMCore reference;
	
	public SRJMCore(SRJMConfiguration config, PublishBus publishBus) {
		super(config.getHost(), config.getPort());
		initBus(config.getServices(), publishBus);
		reference = this;
	}
	
	public SRJMCore (SRJMConfiguration config){
		this(config, null);
	}
	
	
	@SuppressWarnings("unchecked")
	private void initBus(List<JSONWorkerDescription> services, PublishBus publishBus){
		bus = new JSONStringBus(publishBus);
		if (services != null){
			for (JSONWorkerDescription desc: services){
			
					try {
						bus.registerWorker(JSONWorkerGenerator.getWorker(desc));
					} catch (JSONWorkerGeneratorException e) {
						logger.error("Unable to start worker for {}: {}", desc.getUri(), e);
					}
		
			}
		}
		else{
			logger.warn("No workers to init");
		}
	}
	
	@Override 
	public Response serve(IHTTPSession session) {
		 Map<String, String> contents = new HashMap<String, String>();
		/* if (!uri.equals(session.getUri())){
			 logger.error("Recieved incorrect uri: {}", uri);
			 return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not found");
		 }*/
		if (!NanoHTTPD.Method.POST.equals(session.getMethod())){
			logger.error("HTTP Method incorrect: {}",session.getMethod() );
			return new NanoHTTPD.Response(NanoHTTPD.Response.Status.METHOD_NOT_ALLOWED,NanoHTTPD.MIME_PLAINTEXT, "Only allowed method is POST" );
		}
			 
		if (!"application/json".equals(session.getHeaders().get("content-type"))){
			 logger.error("Unable to process content that is not json: {}",session.getHeaders().get("content-type"));
			 return new NanoHTTPD.Response(NanoHTTPD.Response.Status.BAD_REQUEST,NanoHTTPD.MIME_PLAINTEXT, "Content-Type must be application/json" ); 
		}
		try {
		 	logger.debug("Parsing request content");
			session.parseBody(contents);
		} catch (IOException | ResponseException e) {
			logger.error("Error while parsing content: {}", e);
			return new NanoHTTPD.Response(NanoHTTPD.Response.Status.INTERNAL_ERROR,NanoHTTPD.MIME_PLAINTEXT, "Internal Error: "+e );
		}
		String body = contents.get("postData");
		logger.debug("About to process data: {}", body);
		String result = null; 
		try {
			logger.debug("Sending request to Bus");
			result = bus.getResponse(session.getUri(), body);
			return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "application/json", result);	 	 
		} catch (SynchronousBusException e) {
			logger.error("Error while trying to process request: {}",e);
			return new NanoHTTPD.Response(NanoHTTPD.Response.Status.INTERNAL_ERROR,NanoHTTPD.MIME_PLAINTEXT, "Internal Error: "+e );
		} catch (NoListenerException e){
			logger.error("Error while trying to process request: {}",e);
			return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND,NanoHTTPD.MIME_PLAINTEXT, "Service not found: "+session.getUri() );
		}
	}
	
	public void startCore(){
		run = true;
		Thread server = new Thread(){
			
			@Override 
			public void run(){
				try {
					reference.start();
					while (run){
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							logger.debug("Interrupted while sleeping {}", e);
						}
					}
				} catch (IOException e) {
					logger.debug("Unable to start NanoHTTPD server: {}", e);
				}
			}
		};
		server.setName("Core-HTTP-Server-Thread");
		server.start();
	}
	
	public void stopCore(){
		run = false;
		stop();
	}

}
